package base;

import java.util.Random;

/**
 * Element contains informations that have to be analyzed. Created in
 * {@link Source} they end their way in the process of a {@link Server}.
 * 
 * @author rweiller
 */
public class Element implements Cloneable
{
	private static int			GLOBAL_ID					= 1;
	private static final int	possible_element_weight[]	= Element.getElementTableWeight();
	private static final Random	rd							= new Random(Simulator.getIntProperty("seed.element"));

	/**
	 * The identification number of the element
	 */
	private int					id;
	/**
	 * The weight of the element
	 */
	private int					weight;
	/**
	 * When the element goes into the load balancer' queue
	 */
	private double				date_in;
	/**
	 * When the element goes out of the load balancer and enters in a mirror
	 * server
	 */
	private double				date_out;
	// TODO define proceed_time
	private double				proceed_time;
	/**
	 * When the element goes out of the mirror server
	 */
	private double				date_end;

	/**
	 * Constructor
	 */
	public Element()
	{
		this.id = GLOBAL_ID++;
		assignWeight();
	}

	/**
	 * Assignment of a weight to the element, depending on the table of weight
	 * probabilities
	 */
	private void assignWeight()
	{
		double aNumber = rd.nextDouble();
		double weight_chance[] = generateWeightChance();
		double sum_weight_chance = 0;

		for (int i = 0; i <= possible_element_weight.length; i++)
		{
			sum_weight_chance += weight_chance[i];
			if (aNumber < sum_weight_chance)
			{
				weight = possible_element_weight[i];
				break;
			}
		}
	}

	/**
	 * Generates a table of element's weight probabilities
	 * 
	 * @return A table of element's weight probabilities
	 */
	private double[] generateWeightChance()
	{
		double weight_chance[] = new double[possible_element_weight.length];
		double constant_k = generateConstantK();

		for (int i = 0; i < possible_element_weight.length; i++)
		{
			weight_chance[i] = (double) constant_k / (possible_element_weight[i] + id);
		}
		return weight_chance;
	}

	/**
	 * Computes the constant "K"
	 * 
	 * @return The constant "K"
	 */
	private double generateConstantK()
	{
		double sum = 0;
		for (int i = 0; i < possible_element_weight.length; i++)
		{
			sum += (double) 1 / (possible_element_weight[i] + (id));
		}
		return 1 / sum;
	}

	public Object clone()
	{
		Element el = null;
		try
		{
			el = (Element) super.clone();
		} catch (CloneNotSupportedException cnse)
		{
			cnse.printStackTrace(System.err);
		}

		el.id = GLOBAL_ID++;
		return el;
	}

	// setters and getters

	private static int[] getElementTableWeight()
	{
		String s[] = Simulator.getProperty("element.table.weight").split(";");
		int table_weights[] = new int[s.length];
		for (int i = 0; i < s.length; i++)
		{
			table_weights[i] = Integer.parseInt(s[i]);
		}
		return table_weights;
	}

	public static void resetGlobalId()
	{
		GLOBAL_ID = 1;
	}

	public void setDateIn(double date_in)
	{
		this.date_in = date_in;
	}

	public void setDateOut(double date_out)
	{
		this.date_out = date_out;
	}

	public void setWeight(int weight) throws IllegalArgumentException
	{
		if (weight <= 0)
		{
			throw new IllegalArgumentException("Parameters weight should be higher than 0");
		}
		this.weight = weight;
	}

	public int getId()
	{
		return id;
	}

	public double getDateIn()
	{
		return date_in;
	}

	public double getDateOut()
	{
		return date_out;
	}

	public int getWeight()
	{
		return weight;
	}

	public void setDateEnd(double date_end)
	{
		this.date_end = date_end;
	}

	public double getDateEnd()
	{
		return date_end;
	}

	public void setProceedTime(double proceed_time)
	{
		this.proceed_time = proceed_time;
	}

	public double getProceedTime()
	{
		return proceed_time;
	}
}
